﻿using System;
using System.Windows.Input;
using System.Collections.Generic;

namespace Mad.Nug.ViewModel.Base
{
    public class DelegateCommand : ICommand
    {
        #region Private members

        private readonly Action _executeMethod = null;
        private readonly Func<bool> _canExecuteMethod = null;
        private List<WeakReference> _canExecuteChangedHandlers;

        #endregion

        #region Constructors

        /// <summary>
        ///     Constructor
        /// </summary>
        public DelegateCommand(Action executeMethod)
            : this(executeMethod, null)
        {
        }

        /// <summary>
        ///     Constructor
        /// </summary>
        public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
        {
            if (executeMethod == null)
            {
                throw new ArgumentNullException("executeMethod");
            }

            _executeMethod = executeMethod;
            _canExecuteMethod = canExecuteMethod;
        }
        #endregion

        #region Public Methods

        /// <summary>
        ///     Method to determine if the command can be executed
        /// </summary>
        public bool CanExecute()
        {
            if (_canExecuteMethod != null)
            {
                return _canExecuteMethod();
            }
            return true;
        }

        /// <summary>
        ///     Execution of the command
        /// </summary>
        public void Execute()
        {
            if (_executeMethod != null)
            {
                _executeMethod();
            }
        }


        /// <summary>
        ///     Raises the CanExecuteChaged event
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
            OnCanExecuteChanged();
        }

        /// <summary>
        ///     Protected virtual method to raise CanExecuteChanged event
        /// </summary>
        protected virtual void OnCanExecuteChanged()
        {
            if (_canExecuteChangedHandlers != null)
            {
                // Take a snapshot of the handlers before we call out to them since the handlers
                // could cause the array to me modified while we are reading it.

                EventHandler[] callees = new EventHandler[_canExecuteChangedHandlers.Count];
                int count = 0;

                for (int i = _canExecuteChangedHandlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = _canExecuteChangedHandlers[i];
                    EventHandler handler = reference.Target as EventHandler;
                    if (handler == null)
                    {
                        // Clean up old handlers that have been collected
                        _canExecuteChangedHandlers.RemoveAt(i);
                    }
                    else
                    {
                        callees[count] = handler;
                        count++;
                    }
                }

                // Call the handlers that we snapshotted
                for (int i = 0; i < count; i++)
                {
                    EventHandler handler = callees[i];
                    handler(null, EventArgs.Empty);
                }
            }
        }

        #endregion

        #region ICommand Members

        /// <summary>
        ///     ICommand.CanExecuteChanged implementation
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecuteChangedHandlers == null)
                {
                    _canExecuteChangedHandlers = new List<WeakReference>(2);
                }

                _canExecuteChangedHandlers.Add(new WeakReference(value));

            }
            remove
            {
                if (_canExecuteChangedHandlers != null)
                {
                    for (int i = _canExecuteChangedHandlers.Count - 1; i >= 0; i--)
                    {
                        WeakReference reference = _canExecuteChangedHandlers[i];
                        EventHandler existingHandler = reference.Target as EventHandler;
                        if ((existingHandler == null) || (existingHandler == value))
                        {
                            // Clean up old handlers that have been collected
                            // in addition to the handler that is to be removed.
                            _canExecuteChangedHandlers.RemoveAt(i);
                        }
                    }
                }
            }
        }

        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute();
        }

        void ICommand.Execute(object parameter)
        {
            Execute();
        }

        #endregion
    }
}
